package checkers3d.presentation;

import checkers3d.logic.*;
import checkers3d.storage.*;
import java.awt.*;

/**
 * A class that manages the ISoundEngine and IInteractiveWindow's interaction
 * with GameMaster. GameEngine instances the needed objects and starts the game
 * by polling the GameMaster at regular intervals to draw the top GUIContainer.
 * FPS is displayed. Once GameEngine detects that the GameMaster is finished, it
 * cleans up the enviroment and closes the game. GameEngine also redirects input
 * from the InteractiveWindow to the GameMaster while the game is active.
 *
 * @author      Ruben Acuna
 */
public class GameEngine implements IInputObserver
{
    /**
    * IInteractiveWindow that we use for rendering and which provides input.
    */
    private IInteractiveWindow activeInteractiveWindow;

    /**
    * ISoundEngine that we use to play sounds.
    */
    private ISoundEngine activeSoundEngine;

    /**
    * The GameMaster object that GameEngine is in charge of rendering and
    * supplying with input.
    */
    private GameMaster checkers;

    public static final Point WINDOW_SIZE = new Point(1024, 768);//1024x768

    /**
    * Number of milliseconds the GameEngine thread should sleep between renders.
    */
    private final int SLEEP_TIME = 20;

     /**
    * Location on window where FPS is drawn.
    */
    private final Point FPS_LOCATION = new Point(5, 10);

    /**
    * Class constructor that creates the render window and sound objects, and
    * then starts rendering the game state.
    */
    public GameEngine()
    {
        activeInteractiveWindow = new InteractiveWindowNative();
        activeSoundEngine = new SoundEngineNull();
        checkers = GameMaster.getInstance();

        activeInteractiveWindow.start("Checkers 3D", WINDOW_SIZE, this);
        activeSoundEngine.start();

        //Start rendering.
        run();
    }

    /**
    * Main function that starts the game by instancing GameEngine.
    *
    * @param  args  An array containing the arguments that were passed at the
    *               command line.
    */
    public static void main(String[] args)
    {
        new GameEngine();
    }

    /**
    * Handles a click within the interactive window by funneling it to the
    * GameMaster.
    *
    * @param  position  Point object containing the relative coordinates of the
    *                   click.
    * @see IInputObserver
    */
    public void onClick(Point position)
    {
        checkers.onClick(position);
    }

    /**
    * Handles a key press, when the window is in focus, by funneling it to the
    * GameMaster.
    *
    * @param  key       The character of the key that was pressed.
    * @see IInputObserver
    */
    public void onKeyPress(char key)
    {
        checkers.onKeyPress(key);
    }

    /**
    * Renders the GameMaster's top GUIContainer and tracks FPS. Detects when
    * GameMaster is finished and shuts down InteractiveWindow and SoundEngine.
    */
    public void run()
    {
        GUILabel fpsDisplay = new GUILabel("", Color.WHITE, FPS_LOCATION);
        int fpsFrameCount = 0;
        double fpsAverage = 0;
        long fpsLastUpdateTime = System.currentTimeMillis();
        long fpsStartRenderTime;

        long upsLastUpdateTime = System.currentTimeMillis();

        activeInteractiveWindow.prepareBackBuffer();

        while (checkers.getAlive())
        {
            fpsStartRenderTime = System.currentTimeMillis();

            activeInteractiveWindow.draw(checkers.getTopGUIContainer());

            //Update fpsDisplay's RenderResource with the current fps.
            fpsDisplay.setText("FPS:" + fpsAverage);

            activeInteractiveWindow.draw(fpsDisplay);

            checkers.tick((int)(System.currentTimeMillis() - upsLastUpdateTime));
            upsLastUpdateTime = System.currentTimeMillis();

            activeInteractiveWindow.flushBackBuffer();

            //Track fps.
            fpsFrameCount++;
            if (fpsStartRenderTime > fpsLastUpdateTime + 1000L)
            {
                fpsLastUpdateTime = fpsStartRenderTime;
                fpsAverage = fpsFrameCount;
                fpsFrameCount = 0;
            }

            //Make the thread sleep, so other threads can use the processor.
            try
            {
                Thread.sleep(SLEEP_TIME);
            }
            catch (InterruptedException fail)
            {
                Logger.log(toString() + " - " + fail.getMessage());
            }
        }

        activeSoundEngine.shutdown();
        activeInteractiveWindow.shutdown();
    }

    /**
    * Returns a string representation of the GameEngine which contains
    * information about the sound engine, interactive window, and gamemaster.
    *
    * @return       A string representation of this GameEngine.
    */
    @Override
    public String toString()
    {
        return getClass().getName() + " -> "
                                    + activeSoundEngine.toString()
                                    + ", " + activeInteractiveWindow.toString()
                                    + ", " + checkers.toString();
    }
}